//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <aura.h>
#include "private.h"
#include <sys/time.h>
#include <linux/futex.h>
#include <unistd.h>
#include <linux/unistd.h>
#include <errno.h>
#include <pthread.h>
#include <time.h>

typedef struct _CriticalSection {
    pthread_mutex_t posixMutex;
} CriticalSection;

typedef struct _MutexDescr {
    int count;
    int recursive;
    Aura_PVoid pOwner;
    pthread_mutex_t posixMutex;
    pthread_cond_t posixCond;
}MutexDescr;


extern Aura_UInt32 g_ThreadDescrSlot;

AURA_API AuraMutex_Create(
        /* [out] */ Aura_Handle * phMutex)
{
    MutexDescr * pMutex = NULL;

    assert(NULL != phMutex);

    pMutex = (MutexDescr *)Aura_malloc(sizeof(MutexDescr));
    if (NULL == pMutex) {
        return AURA_E_OUT_OF_MEMORY;
    }

    pMutex->count = 1;
    pMutex->recursive = 0;
    pMutex->pOwner = NULL;

    pthread_mutex_init(&pMutex->posixMutex, NULL);
    pthread_cond_init(&pMutex->posixCond, NULL);

    *phMutex = (Aura_Handle)(pMutex);

    return AURA_NOERROR;
}

AURA_API AuraMutex_Lock(
        /* [in] */ Aura_Handle hMutex,
        /* [in] */ Aura_Millisecond32 timeOut,
        /* [out] */ Aura_WaitResult * pResult)
{
    MutexDescr * pMutex = (MutexDescr *)hMutex;
    Aura_PVoid pCurThread = NULL;
    struct timespec ts, tmp;
    int mseconds = 0;
    int ret = 0;

    assert(NULL != pResult);
    assert(NULL != pMutex);

    mseconds = InfiniteFilter(timeOut);
    ts.tv_sec  = (time_t)mseconds/1000;
    ts.tv_nsec = (time_t)(mseconds%1000)*1000*1000;

    clock_gettime(CLOCK_REALTIME, &tmp);
    ts.tv_sec += tmp.tv_sec;
    ts.tv_nsec += tmp.tv_nsec;

    *pResult = Aura_WaitResult_OK;

    AuraTls_GetValue(g_ThreadDescrSlot, &pCurThread);

#if _DEBUG
    ret = 
#endif

    pthread_mutex_lock(&(pMutex->posixMutex));

#if _DEBUG
    assert(0 == ret);
#endif

    if (pMutex->pOwner == pCurThread) {
        pMutex->recursive++;
        pthread_mutex_unlock(&(pMutex->posixMutex));
        return AURA_NOERROR;
    }

    if (--pMutex->count >= 0) {
        goto NORMAL_EXIT;
    }

    ret = pthread_cond_timedwait(&(pMutex->posixCond), &(pMutex->posixMutex), &ts);
    if (ret != 0) {
        if (ETIMEDOUT == ret) {
            *pResult = Aura_WaitResult_TimedOut;
            goto E_PASSIVE_EXIT;
        }
        else if (EINTR == ret) {
            *pResult = Aura_WaitResult_Interrupted;
            goto E_PASSIVE_EXIT;
        }
        else {
            goto E_FAIL_EXIT;
        }
    }

NORMAL_EXIT:
    pMutex->pOwner = pCurThread;
    pMutex->recursive = 1;
    pthread_mutex_unlock(&(pMutex->posixMutex));
    return AURA_NOERROR;

E_PASSIVE_EXIT:
    pMutex->count++;
    pthread_mutex_unlock(&pMutex->posixMutex);
    return AURA_NOERROR;
    
E_FAIL_EXIT:
    pMutex->count++;
    pthread_mutex_unlock(&pMutex->posixMutex);
    return GetLastAuraECode();
}

AURA_API AuraMutex_Unlock(
        /* [in] */ Aura_Handle hMutex)
{
    MutexDescr *pMutex = (MutexDescr *)hMutex;
    Aura_PVoid pCurThread = NULL;
    int ret = 0;

    assert(NULL != pMutex);

    AuraTls_GetValue(g_ThreadDescrSlot, &pCurThread);

#if _DEBUG
    ret =
#endif
    pthread_mutex_lock(&pMutex->posixMutex);
    
#if _DEBUG
    assert(0 == ret);
#endif

    if (pCurThread != pMutex->pOwner) {
        pthread_mutex_unlock(&pMutex->posixMutex);
        return AURA_E_NOT_OWNER;
    }

    if ((pCurThread == pMutex->pOwner)
         && (--pMutex->recursive > 0)) {
        pthread_mutex_unlock(&pMutex->posixMutex);
        return AURA_NOERROR;
    }



    if (++pMutex->count > 0) {
        pMutex->pOwner = NULL;
        goto NORMAL_EXIT;
    }

    pMutex->pOwner = NULL;
    pthread_cond_signal(&pMutex->posixCond); //pthread_signal nerver return an error code;

NORMAL_EXIT:
    pthread_mutex_unlock(&pMutex->posixMutex);
    return AURA_NOERROR;
}

AURA_API AuraMutex_Destroy(
        /* [in] */ Aura_Handle hMutex)
{
    MutexDescr *pMutex = (MutexDescr *)hMutex;
    int ret = 0;

    assert(NULL != pMutex);

    pthread_cond_broadcast(&pMutex->posixCond);

    ret = pthread_mutex_destroy(&pMutex->posixMutex);
    assert(0 == ret);

    ret = pthread_cond_destroy(&pMutex->posixCond);
    assert(0 == ret);

    Aura_free(pMutex);
    return AURA_NOERROR;
}

AURA_API AuraCriticalSection_Create(
        /* [out] */ Aura_Handle * phCriticalSection)
{
    CriticalSection *pCs = NULL;
    pthread_mutexattr_t mutex_attr;

    assert(NULL != phCriticalSection);

    pCs = (CriticalSection *)Aura_malloc(sizeof(CriticalSection));
    if (NULL == pCs) {
        return AURA_E_OUT_OF_MEMORY;
    }

    pthread_mutexattr_init(&mutex_attr);
    pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE);

    pthread_mutex_init(&pCs->posixMutex, &mutex_attr);
    *phCriticalSection = (Aura_Handle)(pCs);
    return AURA_NOERROR;
}

AURA_API AuraCriticalSection_Enter(
        /* [in] */ Aura_Handle hCriticalSection)
{
    CriticalSection *pCs = (CriticalSection *)hCriticalSection;

    assert(NULL != pCs);

    pthread_mutex_lock(&pCs->posixMutex);
    return AURA_NOERROR;
}

AURA_API AuraCriticalSection_Leave(
        /* [in]  */ Aura_Handle hCriticalSection)
{
    CriticalSection *pCs = (CriticalSection *)hCriticalSection;

    assert(NULL != pCs);

    pthread_mutex_unlock(&pCs->posixMutex);
    return AURA_NOERROR;
}

AURA_API AuraCriticalSection_Destroy(
        /* [in]  */ Aura_Handle hCriticalSection)
{
    CriticalSection *pCs = (CriticalSection *)hCriticalSection;
    int ret = 0;

    assert(NULL != pCs);

    ret = pthread_mutex_destroy(&pCs->posixMutex);
    assert(0 == ret);

    Aura_free(pCs);
    return AURA_NOERROR;
}

